
package com.scs.application.modules.order.service.impl;

import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.scs.application.consts.GlobalConsts;
import com.scs.application.consts.MsgConsts;
import com.scs.application.consts.SerialRuleConsts;
import com.scs.application.core.exceptions.BusinessException;
import com.scs.application.core.global.GlobalParamService;
import com.scs.application.core.model.CommonResult;
import com.scs.application.core.msg.domain.SceneMessage;
import com.scs.application.core.msg.enums.MessageType;
import com.scs.application.core.msg.enums.ReceiverType;
import com.scs.application.core.msg.provider.ISceneMsgPushProvider;
import com.scs.application.core.serial.SerialUtils;
import com.scs.application.core.service.BaseServiceImpl;
import com.scs.application.core.thread.BatchSaveTask;
import com.scs.application.core.thread.DefaultSaveHandler;
import com.scs.application.core.utils.*;
import com.scs.application.modules.base.entity.Hospital;
import com.scs.application.modules.base.entity.HospitalMaterial;
import com.scs.application.modules.base.entity.HospitalThirdPlatform;
import com.scs.application.modules.base.entity.Supplier;
import com.scs.application.modules.base.excel.handler.ExcelImportUtilCustom;
import com.scs.application.modules.base.request.HospitalMaterialRequest;
import com.scs.application.modules.base.request.HospitalSupplierRequest;
import com.scs.application.modules.base.service.*;
import com.scs.application.modules.base.vo.HospitalMaterialVO;
import com.scs.application.modules.base.vo.HospitalSupplierVO;
import com.scs.application.modules.msg.service.PushedRecordService;
import com.scs.application.modules.order.adapter.ThirdPlatformAdapter;
import com.scs.application.modules.order.consts.DictConsts;
import com.scs.application.modules.order.dto.*;
import com.scs.application.modules.order.entity.Delivery;
import com.scs.application.modules.order.entity.PurchaseOrder;
import com.scs.application.modules.order.entity.PurchaseOrderItem;
import com.scs.application.modules.order.entity.PurchaseProcess;
import com.scs.application.modules.order.mapper.PurchaseOrderItemMapper;
import com.scs.application.modules.order.mapper.PurchaseOrderMapper;
import com.scs.application.modules.order.request.PurchaseOrderQueryRequest;
import com.scs.application.modules.order.request.ScanQrcodeRequest;
import com.scs.application.modules.order.service.*;
import com.scs.application.modules.order.vo.ExcelPurchaseOrderVO;
import com.scs.application.modules.order.vo.PurchaseOrderItemVO;
import com.scs.application.modules.order.vo.PurchaseOrderVO;
import com.scs.application.modules.thirdparty.erp.request.*;
import com.scs.application.modules.thirdparty.erp.vo.OrderItemVO;
import com.scs.application.modules.thirdparty.erp.vo.OrderVO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.Serializable;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.scs.application.modules.order.consts.DictConsts.STATUS_UNPASS;

/**
 * 采购订单 service 实现
 */
@Slf4j
@AllArgsConstructor
@Transactional(rollbackFor = Exception.class)
@Service
public class PurchaseOrderServiceImpl extends BaseServiceImpl<PurchaseOrderMapper, PurchaseOrder> implements PurchaseOrderService, InitializingBean {

    private final PurchaseOrderItemMapper purchaseOrderItemMapper;

    private final PurchaseOrderItemService purchaseOrderItemService;

    private final PurchaseProcessService purchaseProcessService;

    private final DeliveryService deliveryService;

    private final HospitalSupplierService hospitalSupplierService;

    private final HospitalMaterialService hospitalMaterialService;

    private final ISceneMsgPushProvider sceneMsgPushProvider;

    private final SupplierService supplierService;

    private final HospitalService hospitalService;

    private final HospitalThirdPlatformService hospitalThirdPlatformService;

    private final ThirdPlatformAdapter thirdPlatformAdapter;

    @Autowired
    private PrintTemplateService printTemplateService;

    @Autowired
    private PushedRecordService pushedRecordService;

    private QueryWrapper<PurchaseOrder> getQueryWrapper(PurchaseOrderQueryRequest request) {
        return Wrappers.<PurchaseOrder>query()
                .eq(StringUtils.isNotBlank(request.getHospitalId()), "hospital_id", request.getHospitalId())
                .like(StringUtils.isNotBlank(request.getCode()), "code", request.getCode())
                .like(StringUtils.isNotBlank(request.getSupplierName()), "supplier_name", request.getSupplierName())
                .eq(request.getSupplierConfirmStatus() != null, "supplier_confirm_status", request.getSupplierConfirmStatus())
                .eq(request.getDeliveryStatus() != null, "delivery_status", request.getDeliveryStatus())
                .eq(request.getStatus() != null, "status", request.getStatus())
                .between(request.getBeginGmtCreate() != null && request.getEndGmtCreate() != null, "gmt_create", request.getBeginGmtCreate(), request.getEndGmtCreate())
                .orderByDesc("code");

    }

    @Override
    public boolean removeCascadeById(Serializable id) {
        purchaseOrderItemMapper.delete(Wrappers.<PurchaseOrderItem>query().eq("po_id", id));
        return super.removeById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeCascadeBatchByIds(Collection<? extends Serializable> idList) {
        purchaseOrderItemMapper.delete(Wrappers.<PurchaseOrderItem>query().in("po_id", idList));
        return super.removeCascadeBatchByIds(idList);
    }

    @Override
    public List<PurchaseOrder> list(PurchaseOrderQueryRequest request) {
        if (UserUtils.isHospital()) {
            request.setHospitalId(UserUtils.getHospitalId());
        }
        List<PurchaseOrder> list = this.list(Wrappers.<PurchaseOrder>query().eq(StringUtils.isNotBlank(request.getHospitalId()), "hospital_id", request.getHospitalId())
                .eq(StringUtils.isNotBlank(request.getCode()), "code", request.getCode())
                .eq(StringUtils.isNotBlank(request.getSupplierName()), "supplier_name", request.getSupplierName())
        );

        return list;
    }

    @Override
    public IPage<PurchaseOrder> page(PurchaseOrderQueryRequest request) {
        request.setHospitalId(UserUtils.getHospitalId());
        if (UserUtils.isHospital()) {
            request.setHospitalId(UserUtils.getHospitalId());
        }
        if (UserUtils.isSupplier()) {
            request.setSupplierId(UserUtils.getSupplierId());
        }

        IPage page = this.page(request.getPage(), getQueryWrapper(request));
        return page;
    }

    @Override
    public IPage<ExcelPurchaseOrderVO> pageExcel(PurchaseOrderQueryRequest request) {
        return this.baseMapper.pageExcel(request.getPage(), request);
    }

    @Override
    public List<ExcelPurchaseOrderVO> listExcel(PurchaseOrderQueryRequest request) {
        request.setHospitalId(UserUtils.getHospitalId());
        return this.baseMapper.listExcel(request);
    }

    @Override
    public PurchaseOrder add(PurchaseOrder entity) {
        String hospitalId = entity.getHospitalId();
        if (StringUtils.isBlank(hospitalId)) {
            hospitalId = UserUtils.getHospitalId();
        }
        Hospital hospital = hospitalService.getById(hospitalId);
        BusinessException.throwErrorIf(hospital == null, "未找到相关医院，请联系运营人员");

        entity.setCode(SerialUtils.generateCode(SerialRuleConsts.ORDER_PO_CODE, SerialOwnerUtils.getHospitalOwner(hospitalId)))
                .setTotalAmount(0.0)
                .setBuyerName(UserUtils.currentUser().getNickname())
                .setSupplierConfirmStatus(DictConsts.STATUS_UNCONFIRM)
                .setDeliveryStatus(DictConsts.STATUS_UNDELIVERY)
                .setStatus(DictConsts.STATUS_UNSUBMIT)
                .setIsClosed(DictConsts.STATUS_NO)
                .setSource(DictConsts.STATUS_MANUAL)
                .setHospitalId(hospitalId)
                .setHospitalName(hospital.getName());
        saveCascadeById(entity);
        return entity;
    }


    @Override
    public PurchaseOrder addForSp(PurchaseOrder entity) {
        if (StringUtils.isBlank(entity.getCode())) {
            entity.setCode(SerialUtils.generateCode(SerialRuleConsts.ORDER_PO_CODE, UserUtils.getSupplier().getId()));
        }

        entity.setFromType(DictConsts.PO_FROM_TYPE_SCP);
        entity.setTotalAmount(0.0)
                .setBuyerName(UserUtils.currentUser().getNickname())
                .setSupplierConfirmStatus(DictConsts.STATUS_UNCONFIRM)
                .setDeliveryStatus(DictConsts.STATUS_UNDELIVERY)
                .setStatus(DictConsts.STATUS_UNSUBMIT)
                .setIsClosed(DictConsts.STATUS_NO)
                .setSource(DictConsts.STATUS_MANUAL)
                .setHospitalId(entity.getHospitalId())
                .setHospitalName(entity.getHospitalName());
        saveOrUpdate(entity);
        return entity;
    }

    @Override
    public void submitOrder(String id) {
        PurchaseOrder byId = getById(id);

        if (byId == null) {
            return;
        }

        //当前订单状态是否是制单状态
        if (byId.getStatus() != DictConsts.STATUS_UNSUBMIT) {
            throw new BusinessException("当前订单已提交，不允许重复提交");
        }

        //明细是否为空
        List<PurchaseOrderItem> list = purchaseOrderItemMapper.selectList(Wrappers.<PurchaseOrderItem>query().eq("po_id", id));
        if (list.isEmpty()) {
            throw new BusinessException("采购订单明细为空，不允许提交");
        }

        //制单状态修改为待审核状态
        PurchaseOrder purchaseOrder = new PurchaseOrder();
        purchaseOrder.setStatus(STATUS_UNPASS)
                .setId(id);

        updateById(purchaseOrder);

    }

    @Override
    public void cancelOrder(String id) {
        //获取本条数据
        PurchaseOrder order = getById(id);
        if (order == null) {
            return;
        }
        //当前订单状态是否是待审核状态
        if (order.getStatus() != STATUS_UNPASS) {
            throw new BusinessException("当前订单状态不支持审核操作");
        }
        //更新状态，添加审核人，审核时间
        PurchaseOrder purchaseOrder = new PurchaseOrder();
        purchaseOrder.setStatus(DictConsts.STATUS_UNSUBMIT)
                .setAuditorName(UserUtils.currentUser().getNickname())
                .setGmtAudited(new Date())
                .setId(id);
        //提交
        updateById(purchaseOrder);

    }

    @Override
    public void passOrder(String id) {
        //获取本条数据
        PurchaseOrder order = getById(id);
        if (order == null) {
            return;
        }
        //当前订单状态是否是待审核状态
        if (order.getStatus() != STATUS_UNPASS) {
            throw new BusinessException("当前订单状态不支持审核操作");
        }
        //更新状态，添加审核人，审核时间
        PurchaseOrder purchaseOrder = new PurchaseOrder();
        purchaseOrder.setStatus(DictConsts.STATUS_SUPPLIER_UNCONFIRM)
                .setAuditorName(UserUtils.currentUser().getNickname())
                .setGmtAudited(new Date())
                .setId(id);
        //提交
        updateById(purchaseOrder);

        // 发送微信消息给供应商
        delayQueue.add(buildDealDelayMessage(order));

    }

    @Override
    public void closeOrder(CloseOrderDto dto) {
        PurchaseOrder purchaseOrder = this.getById(dto.getId());

        // 判断当前订单是否存在
        if (purchaseOrder == null) {
            throw new BusinessException("未查询到相关订单信息");
        }

        // 判断当前订单状态是否完成
        if (purchaseOrder.getStatus() == DictConsts.STATUS_COMPLETE) {
            throw new BusinessException("该订单已完成，无法关闭");
        }

        // 判断当前订单是否关闭
        if (purchaseOrder.getIsClosed() != DictConsts.STATUS_NO) {
            throw new BusinessException("该订单已关闭，无法再次关闭");
        }

        //如果订单确认可供货并且未发货，修改发货单状态为关闭
        if (purchaseOrder.getSupplierConfirmStatus() == DictConsts.STATUS_CONFIRMED_YES && purchaseOrder.getDeliveryStatus() == DictConsts.STATUS_UNDELIVERY) {
            List<Delivery> list = deliveryService.list(Wrappers.<Delivery>query().eq("po_id", purchaseOrder.getId())
                    .eq("hospital_id", purchaseOrder.getHospitalId())
                    .eq("po_code", purchaseOrder.getCode())
            );
            list.forEach(o -> o.setIsClosed(DictConsts.CLOSED_YES).setClosedReason("spd同步关闭采购订单" + purchaseOrder.getCode()));
            boolean b = deliveryService.updateBatchById(list);
        }
        // 判断当前订单是否已经发货 如果flagDeliveryClose == 1 允许关闭已发货订单 否则不允许
        if (dto.getFlagDeliveryClose() != DictConsts.CLOSED_YES) {
            if (purchaseOrder.getDeliveryStatus() != DictConsts.STATUS_UNDELIVERY) {
                throw new BusinessException("该订单已发货，无法关闭");
            }
        } else {
            List<Delivery> deliveryList = deliveryService.list(Wrappers.<Delivery>query().eq("po_id", dto.getId()));
            for (Delivery d : deliveryList) {
                Delivery delivery = new Delivery();
                delivery.setClosedReason(dto.getReason()).setId(d.getId());
                deliveryService.updateById(delivery);
            }
        }

        // 修改是否关闭状态为 关闭，修改订单状态为 完成
        this.update(Wrappers.<PurchaseOrder>update()
                .set("is_closed", DictConsts.CLOSED_YES)
                .set("status", DictConsts.STATUS_COMPLETE)
                .set("closed_reason", dto.getReason())
                .set("close_user", dto.getUser())
                .set("gmt_closed", DateUtils.now())
                .eq("id", purchaseOrder.getId())
        );

        // 推送对象 取消
        PushThirdOrderStatusDTO pushDto = new PushThirdOrderStatusDTO();
        pushDto.setOrder(purchaseOrder);
        pushDto.setPoStatus(DictConsts.TYPE_HOSPITAL_CANCEL);
        // 推送第三方平台
        pushOrderStatusToThirdPlatForm(pushDto);
        // 发送微信消息给供应商
        String title = "您好，您有一笔订单已被" + purchaseOrder.getHospitalName() + "关闭";
        sendClosePurchaseMsg(purchaseOrder.getId(), title, dto.getReason());
    }

    //推送订单状态至第三方平台
    @Override
    public void pushOrderStatusToThirdPlatForm(PushThirdOrderStatusDTO dto) {
        List<HospitalThirdPlatform> list = hospitalThirdPlatformService.list();
        if (list != null && !list.isEmpty()) {
            List<HospitalThirdPlatform> optThirds = list.stream().filter(third -> Long.toString(third.getHospitalId()).equals(UserUtils.getHospitalId()) && third.getOrderScene() == DictConsts.ORDER_PLATFORM_PURCHASE).collect(Collectors.toList());
            if (optThirds != null && !optThirds.isEmpty()) {
                ThirdPlatformService thirdPlatformService = thirdPlatformAdapter.getPlatformService(optThirds.get(0).getOrderPlatform());
                thirdPlatformService.pushOrderStatus(dto, optThirds.get(0));
            }
        }
    }

    /**
     * 二维码 添加采购订单
     * @param request
     * @return
     */
    @Override
    public JSONObject loadPoQrcode(ScanQrcodeRequest request) {
        JSONObject result = new JSONObject();
        List<OrderDTO> dtos = new ArrayList<>();
        List<OrderDTO.OrderItemDTO> itemDtos = new ArrayList<>();
        // 重要参数
        Hospital hospital = UserUtils.getHospital();
        String hospitalId = hospital.getId();
        String qrCodeStr = ZipUtils.unzip(request.getQrCode());

        if(JSON.isValidObject(qrCodeStr)){
            // 主表
            OrderDTO main = JSON.parseObject(qrCodeStr, OrderDTO.class);
            /*log*/System.out.println(main.toString());
            log.debug(main.toString());

            List<PurchaseOrder> list = super.list(Wrappers.<PurchaseOrder>query().select("code").eq("code", main.getPoCode()).eq("hospital_id", hospitalId));
            if (list.size() != 0) {
                throw new BusinessException("采购订单已存在，不可重复添加");
            }
            HospitalSupplierVO supplierVO = hospitalSupplierService.findBySupplierIdAndHospitalId(main.getSupplierKey(), hospitalId);
            if (supplierVO == null) {
                throw new BusinessException("该采购订单无法添加");
            }
            Supplier byId = supplierService.getById(main.getSupplierKey());
            main
                    .setItems(itemDtos)
                    .setSupplierName(byId.getName())
                    .setWarehouseKey("0")
                    .setWarehouseName(hospital.getName())
                    .setIsConsignment("1")
                    .setBuyerName("")
            ;

            dtos.add(main);
            this.isolate(dtos, true);

            PurchaseOrderQueryRequest purchaseOrderQueryRequest = new PurchaseOrderQueryRequest();
            purchaseOrderQueryRequest.setCode(main.getPoCode())
                    .setHospitalId(hospitalId);
            IPage page = this.page(purchaseOrderQueryRequest.getPage(), getQueryWrapper(purchaseOrderQueryRequest));
            result.put("formData", page.getRecords());

        } else if (JSON.isValidArray(qrCodeStr)) {
            // 明细
            OrderDTO dto = new OrderDTO();

            if(StringUtils.isBlank(request.getFormId())){
                throw new BusinessException("请先扫描表头二维码");
            }

            PurchaseOrder po = this.getById(request.getFormId());

            JSONArray array = JSON.parseArray(qrCodeStr);
            for (int i = 0; i < array.size(); i++) {
                if (JSON.isValidObject(array.getString(i))) {
                    JSONObject obj = array.getJSONObject(i);
                    if (!StringUtils.equals(po.getSourceKey(), obj.getString("poId"))) {
                        throw new BusinessException(String.format("请先扫描%s的明细", po.getCode()));
                    }
                }
            }
            array = null;
            List<OrderDTO.OrderItemDTO> detail = JSON.parseArray(qrCodeStr, OrderDTO.OrderItemDTO.class);
            log.debug(detail.toString());

            List<PurchaseOrderItem> listPoItem = purchaseOrderItemMapper.selectList(Wrappers.<PurchaseOrderItem>query().select("source_key").eq("po_id", request.getFormId()));
            // 已添加的明细sourcekey 集合
            List<String> listSourceKey = listPoItem.stream().map(PurchaseOrderItem::getSourceKey).collect(Collectors.toList());

            // 赋值主表id，用以跳过主表的相关操作
            dto.setId(request.getFormId());

            // materialCode 集合
            List<String> listMaterialCode = detail.stream().map(o -> o.getMaterialCode()).distinct().collect(Collectors.toList());
            HospitalMaterialRequest requestHM = new HospitalMaterialRequest();
            requestHM.setHospitalId(hospitalId)
                    .setCodes(listMaterialCode);
            // 本地（mscm）耗材信息
            List<HospitalMaterialVO> listHM = hospitalMaterialService.listVO(requestHM);
            Map<String, HospitalMaterialVO> mapHM = listHM.stream().collect(Collectors.toMap(o -> o.getMaterialCode(), o -> o, (o1, o2) -> o1));
            detail.stream().forEach(o ->{
                if(listSourceKey.contains(o.getSourceKey())){
                    throw new BusinessException("该明细已添加！");
                }

                HospitalMaterialVO hm = mapHM.get(o.getMaterialCode());
                BeanUtils.copyProperties(hm, o, OrderDTO.OrderItemDTO.class);
                o.setId(null);
                o.setSubtotalAmount(o.getQty()*o.getPrice());
            });

            dto.setItems(detail);
            dtos.add(dto);
            this.isolate(dtos, true);

            //修正订单价格
            this.update(Wrappers.<PurchaseOrder>update().set("total_amount", purchaseOrderItemMapper.totalAmount(dto.getId())).eq("id", dto.getId()));
        }else{
            throw new BusinessException("无法识别的信息，");
        }
        return result;
    }

    /**
     * 发送关闭订单消息
     */
    private void sendClosePurchaseMsg(String id, String title, String reason) {
//        PurchaseOrder po = this.getById(id);
//        Supplier supplier = supplierService.getById(po.getSupplierId());
//
//        SceneMessage message = new SceneMessage();
//        message.setSceneCode(MsgConsts.SCENE_ORDER_PO_CLOSE_SUCCESS).setReceiver(supplier.getLoginKey())
//                .setReceiverType(ReceiverType.USER);
//        message.addParam("first", title, MessageType.WECHAT)
//                .addParam("keyword1", po.getCode(), MessageType.WECHAT)
//                .addParam("keyword2", po.getGmtCreate(), MessageType.WECHAT)
//                .addParam("keyword3", reason, MessageType.WECHAT)
//                .addParam("keyword4", new Date(), MessageType.WECHAT)
//                .addParam("remark", "", MessageType.WECHAT);
//
//        sceneMsgPushProvider.push(message);
    }

    /**
     * ERP 关闭订单
     * @param request
     * @return
     */
    @Override
    public CommonResult closeOrderERP(CloseOrderRequest request) {
        PurchaseOrder p = this.getOne(
                Wrappers.<PurchaseOrder>query().eq("code", request.getCode())
                        .eq("hospital_id", request.getHospitalId())
        );
        if (p == null) {
            throw new BusinessException("未找到该单号相关信息，请输入正确的单号");
        }
        CloseOrderDto closeOrderDto = new CloseOrderDto().setId(p.getId()).setReason(request.getCloseReason()).setUser(request.getUser()).setFlagDeliveryClose(request.getFlagDeliveryClose());
        this.closeOrder(closeOrderDto);

        //添加到订单业务进程表 院方关闭订单
        PurchaseProcess purchaseProcess = new PurchaseProcess();
        purchaseProcess.setHospitalId(request.getHospitalId())
                .setHospitalName(p.getHospitalName())
                .setSupplierId(p.getSupplierId())
                .setSupplierName(p.getSupplierName())
                .setPoId(p.getId())
                .setGmtCreate(new Date());
        purchaseProcess.setContent(p.getCode() + "订单被取消").setType(DictConsts.TYPE_HOSPITAL_CANCEL);
        return CommonResult.ok("关闭成功");
    }

    @Override
    public CommonResult orderInfoDownLoad(OrderRequest request) {
        QueryWrapper<PurchaseOrder> query = Wrappers.<PurchaseOrder>query()
                .eq("hospital_id", request.getHospitalId());
        if (Objects.nonNull(request.getOrderCode()) && request.getOrderCode().size() > 0) {
            if (request.getOrderCode().size() > 500){
                return CommonResult.error("OrderCode 超过500长度");
            }
            query.in("code", request.getOrderCode());
        } else if (!StringUtils.isAllBlank(request.getStartDate(), request.getEndDate())) {
            query.between("purchase_date", request.getStartDate(), request.getEndDate());
        } else {
            return CommonResult.error("参数错误");
        }
        List<PurchaseOrder> list = this.list(query);
        List<OrderVO> vos = Lists.newArrayListWithCapacity(list.size());
        for (PurchaseOrder po:
                list) {
            OrderVO vo = new OrderVO();
            BeanUtils.copyProperties(po, vo, OrderVO.class);
            vo.setOrderCode(po.getCode());
            vo.setOrderDate(po.getPurchaseDate());

            List<PurchaseOrderItem> itemList = purchaseOrderItemService.list(Wrappers.<PurchaseOrderItem>query().eq("po_id", po.getId()));
            List<OrderItemVO> voItemList = Lists.newArrayListWithCapacity(itemList.size());
            BeanUtils.copyProperties(itemList, voItemList, OrderItemVO.class);
            vo.setItems(voItemList);
            vos.add(vo);
        }
        return CommonResult.ok("成功", vos);
    }

    @Override
    public CommonResult orderDownloadConfirm(OrderConfirmRequest request) {
        List<ConfirmRequest> confList = request.getConfirm();
        final int len = 500;
        if (confList == null || confList.size() > len) {
            return CommonResult.error("确认订单的长度请保持在" + len + "个以内");
        }
        //转换成map<code,status>
        Map<String, Integer> confStatus = confList.stream().collect(Collectors.toMap(ConfirmRequest::getCode, ConfirmRequest::getStatus, (v1, v2) -> v2));
        //转换成list<code>
        List<String> codeList = confList.stream().map(ConfirmRequest::getCode).collect(Collectors.toList());
        List<PurchaseOrder> list = this.list(Wrappers.<PurchaseOrder>query().eq("hospital_id", request.getHospitalId()).in("code", codeList));
        List<String> poCodeList = list.stream().map(PurchaseOrder::getCode).collect(Collectors.toList());

        for (PurchaseOrder po :
                list) {
            Integer status = confStatus.get(po.getCode());
            po.setStatus(status);
        }
        if(list.size()>0) {
            this.saveOrUpdateBatch(list);
        }
        codeList.removeAll(poCodeList);
        if (codeList.size() > 0) {
            return CommonResult.error("失败",codeList.toString());
        }

        return CommonResult.ok("成功");
    }

    @Override
    public IPage<PurchaseOrder> pageForSupplier(PurchaseOrderQueryRequest request) {
        if (UserUtils.isSupplier()) {
            request.setSupplierId(UserUtils.getSupplierId());
        }
        QueryWrapper<PurchaseOrder> wrapper = Wrappers.<PurchaseOrder>query()
                .notIn("status", DictConsts.STATUS_UNSUBMIT, STATUS_UNPASS)
                .eq(StringUtils.isNotBlank(request.getSupplierId()), "supplier_id", request.getSupplierId())
                .like(StringUtils.isNotBlank(request.getCode()), "code", request.getCode())
                .like(StringUtils.isNotBlank(request.getHospitalName()), "hospital_name", request.getHospitalName())
                .eq(request.getSupplierConfirmStatus() != null, "supplier_confirm_status", request.getSupplierConfirmStatus())
                .eq(request.getDeliveryStatus() != null, "delivery_status", request.getDeliveryStatus())
                .eq(request.getStatus() != null, "status", request.getStatus())
                .between(request.getBeginGmtCreate() != null && request.getEndGmtCreate() != null, "gmt_create", request.getBeginGmtCreate(), request.getEndGmtCreate());
        if (StringUtils.isNotBlank(request.getSidx())) {
            if ("desc".equalsIgnoreCase(request.getOrder())) {
                wrapper.orderByDesc(request.getSidx());
            } else {
                wrapper.orderByAsc(request.getSidx());
            }
        } else {
            wrapper.orderByAsc("purchase_date");
        }


        IPage page = this.page(request.getPage(), wrapper);
        return page;
    }

    @Override
    public IPage<PurchaseOrder> pageForSupplierAdd(PurchaseOrderQueryRequest request) {
        if (UserUtils.isSupplier()) {
            request.setSupplierId(UserUtils.getSupplierId());
        }
        QueryWrapper<PurchaseOrder> wrapper = Wrappers.<PurchaseOrder>query()
                .in("status", DictConsts.STATUS_UNSUBMIT, STATUS_UNPASS)
                .eq(StringUtils.isNotBlank(request.getSupplierId()), "supplier_id", request.getSupplierId())
                .like(StringUtils.isNotBlank(request.getCode()), "code", request.getCode())
                .like(StringUtils.isNotBlank(request.getHospitalName()), "hospital_name", request.getHospitalName())
                .between(request.getBeginGmtCreate() != null && request.getEndGmtCreate() != null, "gmt_create", request.getBeginGmtCreate(), request.getEndGmtCreate());
        if (StringUtils.isNotBlank(request.getSidx())) {
            if ("desc".equalsIgnoreCase(request.getOrder())) {
                wrapper.orderByDesc(request.getSidx());
            } else {
                wrapper.orderByAsc(request.getSidx());
            }
        } else {
            wrapper.orderByAsc("purchase_date");
        }


        IPage page = this.page(request.getPage(), wrapper);
        return page;
    }

    /**
     * 确认可供货
     * @param byId
     * @return
     */
    public boolean able(PurchaseOrder byId) {
        if (byId == null) {
            return true;
        }
        //判断当前状态是否待确认
        if (byId.getSupplierConfirmStatus() != DictConsts.STATUS_UNCONFIRM) {
            throw new BusinessException("当前订单已确认，不允许重复确认");
        }
        //如果订单关闭不允许确认
        if (byId.getIsClosed() != DictConsts.STATUS_NO) {
            throw new BusinessException("该订单已关闭，无法确认供货");
        }
        //更新状态
        PurchaseOrder purchaseOrder = new PurchaseOrder();
        purchaseOrder.setSupplierConfirmStatus(DictConsts.STATUS_CONFIRMED_YES)
                .setStatus(DictConsts.STATUS_SUPPLIER_UNDELIVERY)
                .setSupplierConfirmer(UserUtils.currentUser().getId())
                .setGmtSupplierConfirmed(new Date())
                .setId(byId.getId());

        //制单状态直接发货,赋值审核人、审核时间
        if (DictConsts.STATUS_NO != byId.getStatus()) {
            purchaseOrder.setAuditorName(UserUtils.getSupplier().getName())
                    .setGmtAudited(new Date());
        }
        //提交
        updateById(purchaseOrder);
        log.info("[确认可供货] \t 采购单号:{} 供应商:{}", byId.getCode(), byId.getSupplierName());

        // 发送微信消息给医院
//        sendConfirmMsg(purchaseOrder.getId(), "您的订单已被确认");

        //添加到订单业务进程表 确认可供货
        PurchaseProcess purchaseProcess = new PurchaseProcess();
        purchaseProcess.setHospitalId(byId.getHospitalId())
                .setHospitalName(byId.getHospitalName())
                .setSupplierId(byId.getSupplierId())
                .setSupplierName(byId.getSupplierName())
                .setPoId(byId.getId())
                .setGmtCreate(new Date());
        purchaseProcess.setContent(byId.getCode() + "订单确认可供货").setType(DictConsts.TYPE_SUPPLIER_AUDIT);

        purchaseProcessService.save(purchaseProcess);
        return false;
    }


    /**
     * 发送订单供应商确认消息
     */
    private void sendConfirmMsg(String id, String title) {
        PurchaseOrder po = this.getById(id);
        Hospital hospital = hospitalService.getById(po.getHospitalId());

        SceneMessage message = new SceneMessage();
        message.setSceneCode(MsgConsts.SCENE_ORDER_PO_CONFIRM_SUCCESS).setReceiver(hospital.getLoginKey())
                .setReceiverType(ReceiverType.USER);
        message.addParam("first", title, MessageType.WECHAT)
                .addParam("keyword1", po.getCode(), MessageType.WECHAT)
                .addParam("keyword2", po.getTotalAmount(), MessageType.WECHAT)
                .addParam("keyword3", po.getGmtSupplierConfirmed(), MessageType.WECHAT)
                .addParam("remark", "点击查看详情", MessageType.WECHAT);

        sceneMsgPushProvider.push(message);
    }

    @Override
    public void disable(CloseOrderDto dto) {
        //获取本条数据
        PurchaseOrder purchaseOrder = getById(dto.getId());
        if (purchaseOrder == null) {
            return;
        }

        //判断当前状态是否待确认
        if (purchaseOrder.getSupplierConfirmStatus() != DictConsts.STATUS_UNCONFIRM) {
            throw new BusinessException("当前订单已确认，不允许重复确认");
        }

        // 修改是否关闭状态为 关闭，修改订单状态为 完成, 修改供应商确认状态为 确认不可供货
        this.update(Wrappers.<PurchaseOrder>update()
                .set("is_closed", GlobalConsts.YES)
                .set("status", DictConsts.STATUS_COMPLETE)
                .set("supplier_confirm_status", DictConsts.STATUS_CONFIRMED_NO)
                .set("closed_reason", dto.getReason())
                .set("close_user", dto.getUser())
                .set("gmt_supplier_confirmed", new Date())
                .set("gmt_closed", DateUtils.now())
                .eq("id", purchaseOrder.getId())
        );

        log.info("[确认不可供货] \t 采购单号:{} 供应商:{} 原因：{}", purchaseOrder.getCode(), purchaseOrder.getSupplierName(), dto.getReason());

        // 发送微信消息给医院
        sendConfirmMsg(purchaseOrder.getId(), "您有一条订单已确认不可供货");

        //添加到订单业务进程表 确认不可供货
        PurchaseProcess purchaseProcess = new PurchaseProcess();
        purchaseProcess.setHospitalId(purchaseOrder.getHospitalId())
                .setHospitalName(purchaseOrder.getHospitalName())
                .setSupplierId(purchaseOrder.getSupplierId())
                .setSupplierName(purchaseOrder.getSupplierName())
                .setPoId(purchaseOrder.getId())
                .setGmtCreate(new Date());
        purchaseProcess.setContent(purchaseOrder.getCode() + "订单确认不可供货").setType(DictConsts.TYPE_SUPPLIER_UNDELIVERY);

        purchaseProcessService.save(purchaseProcess);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void batchSaveForDTO(List<OrderDTO> orderDTOS, Boolean isTxt) {
        //对医院加锁，同一家医院同时只能有一个订单进来
        synchronized (UserUtils.getHospitalId().intern()) {
            //数据库中已经存在的订单编号
            List<OrderDTO> existingOrder = Lists.newArrayList();
            List<OrderDTO> supplierOrder = Lists.newArrayList();
            List<PurchaseOrder> orders;

            // 不存在重复的订单数据
            List<OrderDTO> dtos = Lists.newArrayListWithCapacity(orderDTOS.size());
            for (OrderDTO orderDTO : orderDTOS) {
                // 若该条订单编号数据库已存在
                String hospitalId = UserUtils.getHospitalId();
//            String hospitalId = "1244433999238021122";
                orders = this.list(Wrappers.<PurchaseOrder>query().select("code").eq("code", orderDTO.getPoCode()).eq("hospital_id", hospitalId));
                if (orders.size() != 0) {
                    existingOrder.add(orderDTO);
                    continue;
                }
                // 供应商id是否正确
                HospitalSupplierVO supplierVO = hospitalSupplierService.findBySupplierIdAndHospitalId(orderDTO.getSupplierKey(), hospitalId);
                if (supplierVO == null) {
                    supplierOrder.add(orderDTO);
                    continue;
                }

                dtos.add(orderDTO);
            }
            //把异常信息拼接到一个字符串里
            String collectString = "";
            if (!existingOrder.isEmpty()) {
                collectString = collectString + existingOrder.stream().map(o -> o.getPoCode() + "订单编号已经存在，不能重复添加").collect(Collectors.joining(";")) + ";";
                log.warn(collectString);
                //throw new BusinessException(existingOrder.stream().map(o -> o.getPoCode() + "订单编号已经存在，不能重复添加").collect(Collectors.joining(";")));
            }
            if (!supplierOrder.isEmpty()) {
                collectString = collectString + supplierOrder.stream().map(o -> o.getSupplierKey() + "供应商不存在，不能添加").collect(Collectors.joining(";"));
                log.warn(collectString);
                //throw new BusinessException(supplierOrder.stream().map(o -> o.getSupplierKey() + "供应商标识不存在，不能添加").collect(Collectors.joining(";")));
            }
            if (collectString != "") {
                throw new BusinessException(collectString);
            }
            // 保存操作
            isolate(dtos, isTxt);
        }
    }

    @Override
    public List<PurchaseOrder> listForHospital(List<String> codes, Date beginDate) {
        String hospitalId = UserUtils.getHospitalId();
//        String hospitalId = "1244433999238021122";
        return this.list(Wrappers.<PurchaseOrder>query()
                .nested(o -> o.in("status", DictConsts.STATUS_SUPPLIER_UNDELIVERY, DictConsts.STATUS_SUPPLIER_DELIVERYED) // 待发货 已发货
                        .or()
                        .eq("supplier_confirm_status", DictConsts.STATUS_CONFIRMED_NO) // 确认不可供货)
                )
                .eq("hospital_id", hospitalId)
                .isNotNull("source_key")
                .in(codes != null, "code", codes)
                .between((codes == null || codes.isEmpty()) && beginDate != null, "gmt_modified", beginDate, new Date())
        );
    }

    @Override
    public int orderSendCount(PurchaseOrderQueryRequest request) {
        int orderSend = this.count(Wrappers.<PurchaseOrder>query()
                .eq("hospital_id", UserUtils.getHospitalId())
                .eq("status", DictConsts.STATUS_SUPPLIER_DELIVERYED)
                .eq("is_closed", DictConsts.CLOSED_NO)
                .between("gmt_create", request.getBeginGmtCreate(), request.getEndGmtCreate())
        );

        return orderSend;
    }

    @Override
    public PurchaseOrder selectDeliveryCount(PurchaseOrderQueryRequest request) {
        request.setHospitalId(UserUtils.getHospitalId());
        PurchaseOrder purchaseOrder = this.baseMapper.selectDeliveryCountDay(request);
        return purchaseOrder;
    }

    @Override
    public int availableOrdersCount(PurchaseOrderQueryRequest request) {
        int availableOrders = this.count(Wrappers.<PurchaseOrder>query()
                .eq("hospital_id", UserUtils.getHospitalId())
                .eq("supplier_confirm_status", DictConsts.STATUS_CONFIRMED_YES)
                .eq("is_closed", DictConsts.CLOSED_NO)
                .between("gmt_create", request.getBeginGmtCreate(), request.getEndGmtCreate())
        );
        return availableOrders;
    }

    @Override
    public int deliveryCount(PurchaseOrderQueryRequest request) {
        int delivery = this.count(Wrappers.<PurchaseOrder>query()
                .eq("hospital_id", UserUtils.getHospitalId())
                .eq("status", DictConsts.STATUS_SUPPLIER_DELIVERYED)
                .eq("is_closed", DictConsts.CLOSED_NO)
                .between("gmt_create", request.getBeginGmtCreate(), request.getEndGmtCreate())
        );
        return delivery;
    }

    @Override
    public int finishedCount(PurchaseOrderQueryRequest request) {
        int finished = this.count(Wrappers.<PurchaseOrder>query()
                .eq("hospital_id", UserUtils.getHospitalId())
                .eq("status", DictConsts.STATUS_COMPLETE)
                .eq("is_closed", DictConsts.CLOSED_NO)
                .between("gmt_create", request.getBeginGmtCreate(), request.getEndGmtCreate())
        );
        return finished;
    }

    @Override
    public PurchaseOrder finishedAmountCount(PurchaseOrderQueryRequest request) {
        /*PurchaseOrder p = this.getOne(Wrappers.<PurchaseOrder>query().select("sum(total_amount) total_amount")
                .eq("hospital_id", UserUtils.getHospitalId())
                .eq("status", DictConsts.STATUS_COMPLETE)
                .eq("is_closed", DictConsts.CLOSED_NO)
                .between("gmt_create", request.getBeginGmtCreate(), request.getEndGmtCreate())
        );*/
        PurchaseOrder p = this.baseMapper.selectDeliveryAmount(request.setSupplierId(UserUtils.getHospitalId()));
        return p;
    }

    /*@Override
    public List<PurchaseOrder> orderSendStatus() {
        List<PurchaseOrder> list = this.list(Wrappers.<PurchaseOrder>query().select("code", "supplier_confirm_status", "delivery_status", "gmt_create")
                .eq("hospital_id", UserUtils.getHospitalId())
                .eq("is_closed", DictConsts.CLOSED_NO)
        );
        return list;
    }*/

    /*@Override
    public int orderSendCountSupplier(PurchaseOrderQueryRequest request) {
        int orderSendDay = this.count(Wrappers.<PurchaseOrder>query()
                .eq("supplier_id", UserUtils.getSupplierId())
                .eq("status", DictConsts.STATUS_SUPPLIER_UNDELIVERY)
                .eq("is_closed", DictConsts.CLOSED_NO)
                .between("gmt_create", request.getBeginGmtCreate(), request.getEndGmtCreate())
        );

        return orderSendDay;
    }

    @Override
    public PurchaseOrder selectUndeliveryCountSupplier(PurchaseOrderQueryRequest request) {
        request.setSupplierId(UserUtils.getSupplierId());
        PurchaseOrder purchaseOrder = this.baseMapper.selectUndeliveryCountSupplier(request);
        return purchaseOrder;
    }*/

    @Override
    public int availableOrdersCountSupplier(PurchaseOrderQueryRequest request) {
        int availableOrders = this.count(Wrappers.<PurchaseOrder>query()
                .eq("supplier_id", UserUtils.getSupplierId())
                .eq("supplier_confirm_status", DictConsts.STATUS_CONFIRMED_YES)
                .eq("is_closed", DictConsts.CLOSED_NO)
                .between("gmt_create", request.getBeginGmtCreate(), request.getEndGmtCreate())
        );
        return availableOrders;
    }

    @Override
    public int deliveryCountSupplier(PurchaseOrderQueryRequest request) {
        int delivery = this.count(Wrappers.<PurchaseOrder>query()
                .eq("supplier_id", UserUtils.getSupplierId())
                .eq("status", DictConsts.STATUS_SUPPLIER_DELIVERYED)
                .eq("is_closed", DictConsts.CLOSED_NO)
                .between("gmt_create", request.getBeginGmtCreate(), request.getEndGmtCreate())
        );
        return delivery;
    }

    @Override
    public int finishedCountSupplier(PurchaseOrderQueryRequest request) {
        int finished = this.count(Wrappers.<PurchaseOrder>query()
                .eq("supplier_id", UserUtils.getSupplierId())
                .eq("status", DictConsts.STATUS_COMPLETE)
                .eq("is_closed", DictConsts.CLOSED_NO)
                .between("gmt_create", request.getBeginGmtCreate(), request.getEndGmtCreate())
        );
        return finished;
    }

    @Override
    public PurchaseOrder finishedAmountCountSupplier(PurchaseOrderQueryRequest request) {
        /*PurchaseOrder p = this.getOne(Wrappers.<PurchaseOrder>query().select("sum(total_amount) total_amount")
                .eq("supplier_id", UserUtils.getSupplierId())
                .eq("status", DictConsts.STATUS_COMPLETE)
                .eq("is_closed", DictConsts.CLOSED_NO)
                .between("gmt_create", request.getBeginGmtCreate(), request.getEndGmtCreate())
        );*/
        PurchaseOrder p = this.baseMapper.selectDeliveryAmount(request.setSupplierId(UserUtils.getSupplierId()));
        return p;
    }

    /**
     * ERP获取订单
     * @param orderDate
     * @param codes
     * @return
     */
    @Override
    public CommonResult<List<PurchaseOrderVO>> getOrdersERP(String hospitalId, String orderDate, String[] codes) {
        List<PurchaseOrderVO> result = null;
        if(codes != null && codes.length!=0 ){
            //执行查询  code
            List<PurchaseOrder> list = this.list(Wrappers.<PurchaseOrder>query().eq("hospital_id", hospitalId).in("code", codes));
            result = getPurchaseOrderItemVo(list);
        }else if(orderDate != null){
            //执行查询 orderDate
            String date = null;
            try {
                date = DateUtils.formatToAnotherPattern(orderDate, "yyyy-MM-dd","yyyy-MM-dd");
            } catch (ParseException e) {
                return CommonResult.error("日期格式错误！");
            }
            List<PurchaseOrder> list = this.list(Wrappers.<PurchaseOrder>query().eq("hospital_id", hospitalId).eq("purchase_date", date));
            result = getPurchaseOrderItemVo(list);
        }else{
            return CommonResult.error("缺少必要参数");
        }
        return CommonResult.ok("查询成功", result);
    }

    /**
     * ERP获取订单明细
     * @param list
     * @return
     */
    private List<PurchaseOrderVO> getPurchaseOrderItemVo(List<PurchaseOrder> list) {
        List<PurchaseOrderVO> result = Lists.newArrayListWithCapacity(list.size());
        BeanUtils.copyProperties(list, result, PurchaseOrderVO.class);
        //查询并添加明细
        result.forEach(o -> {
            List<PurchaseOrderItem> item = purchaseOrderItemService.list(Wrappers.<PurchaseOrderItem>query().eq("po_id", o.getId()));
            List<PurchaseOrderItemVO> itemVo = Lists.newArrayListWithCapacity(item.size());
            BeanUtils.copyProperties(item, itemVo, PurchaseOrderItemVO.class);
            o.setItems(itemVo);
        });
        return result;
    }

    /**
     * 第三方接口回传状态（erp）
     * @param list
     * @return
     */
    @Override
    public CommonResult callStatus(List<OrderStatusRequest> list) {
        Iterator<OrderStatusRequest> requestIterator = list.iterator();
        while (requestIterator.hasNext()){
            OrderStatusRequest request = requestIterator.next();
            PurchaseOrder p = getOne(
                    Wrappers.<PurchaseOrder>query().eq("code", request.getPocode())
                            .eq("hospital_id", request.getHospitalId())
            );
            if (p == null){
                throw new BusinessException("未找到该单号相关信息，请输入正确的单号");
            }

            Integer status = p.getStatus();
            // 更新前的校验
            if(status == DictConsts.TYPE_HOSPITAL_CANCEL || status == DictConsts.TYPE_COMPLETE){
                throw new  BusinessException("该订单已完成或取消，状态不可变更");
            }


            PurchaseProcess purchaseProcess = new PurchaseProcess();
            purchaseProcess.setHospitalId(p.getHospitalId())
                    .setHospitalName(p.getHospitalName())
                    .setSupplierId(p.getSupplierId())
                    .setSupplierName(p.getSupplierName())
                    .setPoId(p.getId())
                    .setGmtCreate(new Date());

            // 更新相应状态
            switch (request.getPostatus()){
                case DictConsts.TYPE_SUPPLIER_UNAUDIT:
                    // 初始状态
                    break;
                case DictConsts.TYPE_SUPPLIER_AUDIT:
                    this.able(p);
                    break;
                case DictConsts.TYPE_SUPPLIER_UNDELIVERY:
                    // 确认不可供货
                    CloseOrderDto orderDto = new CloseOrderDto();
                    orderDto.setId(p.getId());
                    orderDto.setReason("第三方确认不可供货");
                    this.disable(orderDto);
                    break;
                case DictConsts.TYPE_SUPPLIER_ANY_DELIVERYED:
                    // 部分发货
                    PurchaseOrder purchaseOrder = new PurchaseOrder();
                    purchaseOrder
                            .setDeliveryStatus(DictConsts.STATUS_PART_DELIVERY) // 发货状态-部分发货
                            .setStatus(DictConsts.STATUS_SUPPLIER_DELIVERYED)// 已发货
                            .setId(p.getId());
                    this.updateById(purchaseOrder);

                    purchaseProcess.setContent(p.getCode() + "订单供应商部分发货");
                    purchaseProcess.setType(DictConsts.TYPE_SUPPLIER_ANY_DELIVERYED);
                    break;
                case DictConsts.TYPE_SUPPLIER_ALL_DELIVERYED:
                    // 全部发货
                    purchaseOrder = new PurchaseOrder();
                    purchaseOrder
                            .setDeliveryStatus(DictConsts.STATUS_DELIVERYED) // 发货状态-完全发货
                            .setStatus(DictConsts.STATUS_COMPLETE)// 完成
                            .setId(p.getId());
                    this.updateById(purchaseOrder);

                    purchaseProcess.setContent(p.getCode() + "订单供应商全部发货");
                    purchaseProcess.setType(DictConsts.TYPE_SUPPLIER_ALL_DELIVERYED);
                    break;
                case DictConsts.TYPE_HOSPITAL_CANCEL:
                    CloseOrderDto closeOrderDto = new CloseOrderDto();
                    closeOrderDto.setId(p.getId());
                    closeOrderDto.setReason("第三方取消订单");
                    this.closeOrder(closeOrderDto);
                    break;
                case DictConsts.TYPE_COMPLETE:
                    break;
                default:
                    return CommonResult.ok("不支持的订单状态！");
            }

            if(StringUtils.isBlank(purchaseProcess.getContent())){
                purchaseProcessService.updateById(purchaseProcess);
            }
        }


        return CommonResult.ok("订单状态回传成功");
    }

    /**
     * 事务隔离
     *
     * @param dtos
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void isolate(List<OrderDTO> dtos, boolean isTxt) {
        // 订单数据
        List<PurchaseOrder> list = Lists.newArrayListWithCapacity(dtos.size());
        List<PurchaseProcess> listProcess = Lists.newArrayListWithCapacity(dtos.size());

        boolean unSaveMain = dtos.size() == 1 && StringUtils.isNoneBlank(dtos.get(0).getId());
        if (unSaveMain){
            // 当OrderDTO.id字段有值时候
        }else{
            PurchaseOrder order;
            // 订单详情数据
            for (OrderDTO orderDTO : dtos) {
                order = new PurchaseOrder();
                BeanUtils.copyProperties(orderDTO, order);
                if (order.getGmtRequireLatestDelivery() == null) {
                    order.setGmtRequireLatestDelivery(DateUtils.addDays(DateUtils.now(), 3));
                }
                order.setSource(DictConsts.STATUS_INTERFACE)
                        .setCode(orderDTO.getPoCode())
                        .setSupplierId(orderDTO.getSupplierKey()) //数据统一标识
                        .setWarehouseId(orderDTO.getWarehouseKey())
                        .setSupplierConfirmStatus(DictConsts.STATUS_UNCONFIRM) // 供应商确认状态-待确认
                        .setDeliveryStatus(DictConsts.STATUS_UNDELIVERY) // 发货状态-未发货
                        .setStatus(isTxt ? DictConsts.STATUS_UNSUBMIT : DictConsts.STATUS_SUPPLIER_UNCONFIRM) // 订单状态-供应商待确认
                        .setIsClosed(DictConsts.STATUS_NO)
                        .setHospitalId(UserUtils.getHospitalId())
                        .setHospitalName(UserUtils.getHospital().getName())
                        .setRemark(orderDTO.getRemark())
                        .setPackageName(orderDTO.getPackageName())
                        .setFromType(GlobalConsts.SystemType.LMS)
                ;

                // 待确认订单，复制审核时间、审核人
                if (DictConsts.STATUS_SUPPLIER_UNCONFIRM == order.getStatus()) {
                    order.setGmtSubmitted(DateUtils.now()).setAuditorName(null).setGmtAudited(null);
                }

                list.add(order);
            /*if (!isTxt) {
                delayQueue.add(buildDealDelayMessage(order));
            }*/

            }

            // 保存订单主表
            saveBatch(list);
        }
        Map<String, String> map = list.stream().collect(Collectors.toMap(o -> o.getCode(), o -> o.getId(), (o1, o2) -> o1));

        List<PurchaseOrderItem> itemList = new ArrayList<>();
        for (OrderDTO orderDTO : dtos) {
            List<OrderDTO.OrderItemDTO> items = orderDTO.getItems();
            List<PurchaseOrderItem> tmpList = Lists.newArrayListWithExpectedSize(items.size());
            BeanUtils.copyProperties(items, tmpList, PurchaseOrderItem.class);
            BusinessException.throwErrorIf(ObjectUtil.length(tmpList) == 0 ,"采购订单{}没有明细，不需要推送",orderDTO.getPoCode());
            BusinessException.throwErrorIf(tmpList.size() != orderDTO.getCountDtl() ,"采购订单明细记录数量{}与主单据明细记录数量{}不一致，请检查",tmpList.size() +"",orderDTO.getCountDtl() +"");
            for (PurchaseOrderItem item : tmpList) {
                //根据materialcode 查询出耗材信息
                if (StringUtils.isBlank(item.getMaterialCode())) {
                    continue;
                }
                HospitalMaterialRequest request = new HospitalMaterialRequest().setCode(item.getMaterialCode());
                List<HospitalMaterialVO> hm = hospitalMaterialService.listVO(request);
                if (hm.isEmpty()) {
                    throw new BusinessException("耗材编码" + item.getMaterialCode() + "耗材" + item.getMaterialName() + "品规" + item.getMaterialSpec() + "不存在");
                } else if (hm.size() > 1) {
                    throw new BusinessException("同一医院下同一耗材编码出现多条耗材，请处理");
                } else if (hm.size() == 1) {
                    //如果传过来的注册证号不为空就取传过来的值如果为空就取医院耗材表对应的注册证号
                    if (StringUtils.isNotBlank(item.getCertificateNo())) {
                        continue;
                    }
                    item.setCertificateNo(hm.get(0).getCertificateNo());
                }
            }

            String poId = unSaveMain ? orderDTO.getId() : map.get(orderDTO.getPoCode());
            tmpList.stream().forEach(o -> o.setPoId(poId).setDeliveryStatus(DictConsts.STATUS_UNDELIVERY));
            itemList.addAll(tmpList);

            // 更新主表金额
            double totalAmount = tmpList.stream().mapToDouble(PurchaseOrderItem::getSubtotalAmount).sum();
            this.update(Wrappers.<PurchaseOrder>update().set("total_amount", totalAmount).eq("id", poId));
        }

        // 保存订单明细表
        purchaseOrderItemService.saveBatch(itemList);


        // 推送消息
        list.stream().forEach(o -> {
            pushedRecordService.pushVxPoPassAsync(o, false);
        });


        //保存订单业务到订单业务记录表
        if (!isTxt) {
            for (PurchaseOrder p : list) {
                PurchaseProcess purchaseProcess = new PurchaseProcess();
                purchaseProcess.setHospitalId(UserUtils.getHospitalId())
                        .setHospitalName(UserUtils.getHospital().getName())
                        .setSupplierId(p.getSupplierId())
                        .setSupplierName(p.getSupplierName())
                        .setPoId(p.getId())
                        .setGmtCreate(new Date());
                purchaseProcess.setContent(p.getCode() + "订单供应商待确认").setType(DictConsts.TYPE_SUPPLIER_UNAUDIT);

                listProcess.add(purchaseProcess);
            }

            purchaseProcessService.saveBatch(listProcess);
        }
        if (!unSaveMain) {
            pushOrderToThirdPlatForm(dtos);
        }
    }

    //推送订单至第三方平台
    @Override
    public void pushOrderToThirdPlatForm(List<OrderDTO> dtos) {
        List<HospitalThirdPlatform> list = hospitalThirdPlatformService.list();
        if (list != null && !list.isEmpty()) {
            List<HospitalThirdPlatform> optThirds = list.stream().filter(third -> Long.toString(third.getHospitalId()).equals(UserUtils.getHospitalId()) && third.getOrderScene() == DictConsts.ORDER_PLATFORM_PURCHASE).collect(Collectors.toList());
            if (optThirds != null && !optThirds.isEmpty()) {
                ThirdPlatformService thirdPlatformService = thirdPlatformAdapter.getPlatformService(optThirds.get(0).getOrderPlatform());
                thirdPlatformService.pushOrder(dtos, optThirds.get(0));
            }
        }
    }

    /**
     * 查询未发货订单, 部分发货采购订单
     *
     * @param request
     * @return
     */
    @Override
    public IPage<PurchaseOrder> pageForDelivery(PurchaseOrderQueryRequest request) {

        request.setSupplierId(UserUtils.getSupplierId());
        IPage page = this.page(request.getPage(), Wrappers.<PurchaseOrder>query()
                .eq(StringUtils.isNotBlank(request.getSupplierId()), "supplier_id", request.getSupplierId())
                .like(StringUtils.isNotBlank(request.getCode()), "code", request.getCode())
                .eq("supplier_confirm_status", DictConsts.STATUS_CONFIRMED_YES)
                .ne("delivery_status", DictConsts.STATUS_DELIVERYED)
                .eq("is_closed", DictConsts.STATUS_NO)
                .orderByDesc("gmt_create")
        );
        return page;
    }


    @Override
    protected ExcelImportResult doExcelImport(MultipartFile multipartFile, Map<String, Object> params) throws Exception {
        ImportParams importParams = PoiUtils.getDefaultImportParams();
        ExcelImportResult importResult = ExcelImportUtilCustom.importExcelMore(multipartFile.getInputStream(), ExcelPurchaseOrderDTO.class, importParams);
        List<ExcelPurchaseOrderDTO> dtoList = Lists.newArrayList(importResult.getList());


        List<PurchaseOrderItemDTO> itemDTOS = Lists.newArrayListWithCapacity(dtoList.size());
        List<PurchaseOrderItem> items = Lists.newArrayListWithCapacity(itemDTOS.size());

        boolean isHospital = UserUtils.isHospital();

        if (!isHospital) {
            throw new BusinessException("当前用户不是医院用户");
        }

        final String hospitalId = UserUtils.getHospitalId();
        final String hospitalName = UserUtils.getHospital().getName();
        final String buyerName = UserUtils.currentUser().getNickname();

        // 获取院方下供应商信息
        List<HospitalSupplierVO> hospitalSupplierVOList = hospitalSupplierService.listVO(new HospitalSupplierRequest().setHospitalId(hospitalId));
        Map<String, List<HospitalSupplierVO>> hospitalSupplierMap = hospitalSupplierVOList.parallelStream().collect(Collectors.groupingBy(o -> o.getSupplierName()));

        // 根据采购单号分组
        Map<String, List<ExcelPurchaseOrderDTO>> map = dtoList.stream().collect(Collectors.groupingBy(o -> o.getCode()));
        List<List<ExcelPurchaseOrderDTO>> ldtoList = Lists.newArrayListWithCapacity(map.size());
        for (String key : map.keySet()) {
            ldtoList.add(map.get(key));
        }

        DefaultSaveHandler<List<ExcelPurchaseOrderDTO>> saveHandler = new DefaultSaveHandler<List<ExcelPurchaseOrderDTO>>() {

            @Override
            public void doSave(int currentIndex, List<ExcelPurchaseOrderDTO> listEntity) throws BusinessException {
                ExcelPurchaseOrderDTO epoDTO = listEntity.get(0);
                PurchaseOrder order = new PurchaseOrder();
                BeanUtils.copyProperties(epoDTO, order);
                order.setHospitalId(hospitalId).setHospitalName(hospitalName);

                // 校验供应商是否存在
                List<HospitalSupplierVO> supplierVOS = hospitalSupplierMap.get(epoDTO.getSupplierName());

                if (supplierVOS == null || supplierVOS.isEmpty()) {
                    importResult.getList().remove(epoDTO);
                    epoDTO.setErrorMsg(String.format("供应商不存在"));
                    importResult.getFailList().add(epoDTO);
                    log.warn("第{}行供应商不存在，请先确认数据是否正确；供应商名称：{}", epoDTO.getRowNum(), epoDTO.getSupplierName());
                    return;
                }

                // 检验供应商是否启用
                if (GlobalConsts.NO_STR.equals(supplierVOS.get(0).getInactive())) {
                    importResult.getList().remove(epoDTO);
                    epoDTO.setErrorMsg(String.format("供应商未启用"));
                    importResult.getFailList().add(epoDTO);
                    log.warn("第{}行供应商未启用，请先确认数据是否正确；供应商名称：{}", epoDTO.getRowNum(), epoDTO.getSupplierName());
                    return;
                }
                order.setSupplierId(supplierVOS.get(0).getSupplierId())
                        .setBuyerName(buyerName)
                        .setSupplierConfirmStatus(DictConsts.STATUS_UNCONFIRM)
                        .setDeliveryStatus(DictConsts.STATUS_UNDELIVERY)
                        .setStatus(DictConsts.STATUS_UNSUBMIT)
                        .setIsClosed(DictConsts.STATUS_NO)
                        .setWarehouseId("0")
                        .setSource(DictConsts.STATUS_MANUAL);

                // 查询数据库中是否已存在
                PurchaseOrder one = getOne(Wrappers.<PurchaseOrder>query()
                        .eq("hospital_id", order.getHospitalId())
                        .eq(StringUtils.isNotBlank(order.getCode()), "code", order.getCode())
                        .last("limit 1")
                );

                // 存在更新，不存在新增
                if (one != null) {
                    order.setId(one.getId());
                    updateById(order);
                } else {
                    save(order);
                }

                // 获取院方下商品信息
                List<HospitalMaterial> hospitalMaterialList = hospitalMaterialService.listByHospital(hospitalId, order.getSupplierId());
                Map<String, List<HospitalMaterial>> hospitalMaterialMap = hospitalMaterialList.parallelStream().collect(Collectors.groupingBy(o -> o.getMaterialSpec()));

                listEntity.stream().forEach(o -> o.setPoId(order.getId()));
                for (ExcelPurchaseOrderDTO dto : listEntity) {
                    PurchaseOrderItemDTO orderItemDTO = new PurchaseOrderItemDTO();
                    BeanUtils.copyProperties(dto, orderItemDTO);
                    List<HospitalMaterial> specs = hospitalMaterialMap.get(dto.getMaterialSpec());
                    if (specs == null || specs.isEmpty()) {
                        importResult.getList().remove(dto);
                        dto.setErrorMsg(String.format("商品不存在"));
                        importResult.getFailList().add(dto);
                        log.warn("第{}行商品不存在，请先确认数据是否正确；商品名称：{}", dto.getRowNum(), dto.getMaterialName() + dto.getMaterialSpec());
                        return;
                    }

                    // 检验供应商是否启用
                    if (GlobalConsts.NO_STR.equals(specs.get(0).getInactive())) {
                        importResult.getList().remove(dto);
                        dto.setErrorMsg(String.format("商品未启用"));
                        importResult.getFailList().add(dto);
                        log.warn("第{}行商品未启用，请先确认数据是否正确；商品名称：{}", dto.getRowNum(), dto.getMaterialName() + dto.getMaterialSpec());
                        return;
                    }

                    orderItemDTO.setMaterialSpecId(specs.get(0).getMaterialSpecId())
                            .setDeliveredQty(0.0)
                            .setDeliveredAmount(0.0)
                            .setStoredQty(0.0)
                            .setDeliveryStatus(DictConsts.STATUS_UNDELIVERY)
                    ;

                    //查询数据库该订单明细是否存在，存在就更新不存在就保存
                    PurchaseOrderItem item = purchaseOrderItemService.getOne(Wrappers.<PurchaseOrderItem>query()
                            .eq(StringUtils.isNotBlank(dto.getPoId()), "po_id", dto.getPoId())
                            .eq(StringUtils.isNotBlank(dto.getMaterialSpecId()), "material_spec_id", dto.getMaterialSpecId())
                            .last("limit 1")
                    );

                    // 存在更新，不存在新增
                    if (item != null) {
                        orderItemDTO.setId(item.getId());
                    }
                    itemDTOS.add(orderItemDTO);
                }
            }
        };

        BatchSaveTask<ExcelPurchaseOrderDTO> task = new BatchSaveTask(saveHandler, ldtoList);

        this.forkJoinPool.invoke(task);

        BeanUtils.copyProperties(itemDTOS, items, PurchaseOrderItem.class);

        List<PurchaseOrderItem> updateItems = items.stream().filter(o -> StringUtils.isNotBlank(o.getId())).collect(Collectors.toList());
        List<PurchaseOrderItem> insertItems = items.stream().filter(o -> StringUtils.isBlank(o.getId())).collect(Collectors.toList());
        if (!updateItems.isEmpty()) {
            purchaseOrderItemService.updateBatchById(updateItems);
        }
        if (!insertItems.isEmpty()) {
            purchaseOrderItemService.saveBatch(insertItems);
        }
        return importResult;
    }


    /**
     * 用于当供应商订单处理超时 向供应商发送微信提醒
     */
    private final DelayQueue<OrderDealTimeoutMessage> delayQueue = new DelayQueue();

    @Autowired
    private GlobalParamService globalParamService;

    /**
     * 获取超时提醒分钟数
     *
     * @return
     */
    private int getDelayNoticeMinute() {
//        return 1 * 24 * 60;
//        return 2;
        return (int) globalParamService.getLong("order.timeout.notice.intervalMinute", 1 * 24 * 60);

    }


    /**
     * 最大提醒次数
     *
     * @return
     */
    private int getMaxNoticeTimes() {
        // 从配置中获取最大通知次数
        return (int) globalParamService.getLong("order.timeout.notice.maxTimes", 1);
    }

    private OrderDealTimeoutMessage buildDealDelayMessage(PurchaseOrder order) {
        OrderDealTimeoutMessage delayMessage = new OrderDealTimeoutMessage();
        delayMessage.setOrderId(order.getId()).setStartTime(new Date()).setNoticedTimes(0);
        return delayMessage;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 加载一天之类尚未确定的订单
        List<PurchaseOrder> list = this.list(Wrappers.<PurchaseOrder>query()
                .between("gmt_audited", DateUtils.addMinutes(new Date(), -1 * 24 * 60), new Date())
                .eq("supplier_confirm_status", DictConsts.STATUS_UNCONFIRM)
                .lt("supplier_timeout_noticed_times", getMaxNoticeTimes())
        );
        for (PurchaseOrder order : list) {
            delayQueue.add(buildDealDelayMessage(order));
        }
        log.info("共有{}条订单已经超过一天未进行确认了，已加入推送队列", list.size());

        Thread thread = new Thread(() -> {
            while (true) {
                try {
                    OrderDealTimeoutMessage message = delayQueue.take();

                    PurchaseOrder order = this.getById(message.getOrderId());
                    if (order == null) {
                        continue;
                    }
                    if (order.getSupplierTimeoutNoticedTimes() >= getMaxNoticeTimes()) {
                        continue;
                    }

                    // 如果已确认可供货/不可供货 等其他状态，不需要再进行推送
                    if (!(DictConsts.STATUS_UNCONFIRM == order.getSupplierConfirmStatus().intValue())) {
                        continue;
                    }

                    // 更新已通知次数
                    update(Wrappers.<PurchaseOrder>update()
                            .setSql("supplier_timeout_noticed_times = supplier_timeout_noticed_times + 1")
                            .eq("id", order.getId()));

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "OrderDealTimeoutNoticeThread");
        thread.setDaemon(true);
        thread.start();
    }


    @Data
    @Accessors(chain = true)
    private class OrderDealTimeoutMessage implements Delayed {
        // 采购订单ID
        private String orderId;

        // 已发送次数
        private int noticedTimes = 0;

        // 开始时间
        private Date startTime;

        /**
         * 获取延时分钟数，如果在这个时间段之内还没处理完，则不继续进行推送
         *
         * @return
         */
        public int getDelayMinute() {
            if (noticedTimes > getMaxNoticeTimes()) {
                return 0;
            }
            return getDelayNoticeMinute() * (noticedTimes + 1);
        }

        /**
         * 获取下次推送时间
         *
         * @return
         */
        public Date getNextDate() {
            return DateUtils.addMinutes(this.startTime, getDelayMinute());
        }

        @Override
        public long getDelay(TimeUnit unit) {
            return unit.convert((getNextDate().getTime()) - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        }

        @Override
        public int compareTo(Delayed o) {
            return this.getNextDate().compareTo(((OrderDealTimeoutMessage) o).getNextDate());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveAndSend(PurchaseOrder entity,Boolean isSave) {
        synchronized (entity.getId().intern()) {
            List<PurchaseOrderItem> itemList = purchaseOrderItemService.list(Wrappers.<PurchaseOrderItem>query().eq("po_id", entity.getId()));
            BusinessException.throwErrorIf(ObjectUtil.length(itemList) ==0, "订单明细为空，不允许操作");

            if (isSave) {
                updateById(entity);
            }
            //获取本条数据
            PurchaseOrder byId = getById(entity.getId());
            Hospital hospital = hospitalService.getById(byId.getHospitalId());
            BusinessException.throwErrorIf(hospital == null, "采购医院不存在,禁止操作");
            BusinessException.throwErrorIf(hospital.getFlagDisabledOrderAdd()!= null && hospital.getFlagDisabledOrderAdd() , "该医院已禁止采购新增");
            if (able(byId)) return;

            Delivery delivery = new Delivery();
            delivery.setPoCode(byId.getCode())
                    .setSupplierId(byId.getSupplierId()).setSupplierName(byId.getSupplierName())
                    .setHospitalId(byId.getHospitalId()).setHospitalName(byId.getHospitalName())
                    .setPoId(byId.getId()).setOrderAmount(byId.getTotalAmount())
                    .setGmtAudited(byId.getGmtAudited()).setMaterialTypeCode(byId.getMaterialTypeCode())
                    .setDeliveryAmount(byId.getTotalAmount()).setWarehouseId(byId.getWarehouseId())
                    .setWarehouseName(byId.getWarehouseName()).setIsConsignment(byId.getIsConsignment())
                    .setIsOneThingOneYard(byId.getIsOneThingOneYard()).setGmtRequireLatestDelivery(byId.getGmtRequireLatestDelivery())
                    .setPurchaseDeptId(byId.getPurchaseDeptId()).setPurchaseDeptName(byId.getPurchaseDeptName()).setPackageName(byId.getPackageName())
                    .setRemark(byId.getRemark()).setDemandDeptId(byId.getDemandDeptId()).setDemandDeptName(byId.getDemandDeptName()).setPurchaseDate(byId.getPurchaseDate());
            //第二个参数是本次发货单ID
            deliveryService.saveDeliverys(delivery, null);
        }
    }
}
